﻿using System.Collections.Generic;

namespace Red.Controls.TimeLine.Core
{
    class ScaleControler
    {
        #region Data types

        public class ScaleEntry
        {
            public double Unit { get; private set; }
            public double MinSizePx { get; private set; }
            public double MaxSizePx { get; private set; }
            public int Division { get; private set; }

            public ScaleEntry( double unit, double minSizePx, double maxSizePx, int division )
            {
                Unit = unit;
                MinSizePx = minSizePx;
                MaxSizePx = maxSizePx;
                Division = division;
            }
        }

        #endregion

        #region Static readonly

        private readonly static List<ScaleEntry> _scalingTable;

        #endregion

        #region Data

        private ScaleEntry _activeScale = null;
        private double _intervalSize = 0;

        #endregion
        
        #region Static Ctr
        static ScaleControler()
        { 
            // Scaling range:
            // Unit     Max size in px   --   Min size in px
            // -----------------------------------------------      
            _scalingTable = new List<ScaleEntry>{
                                                    new ScaleEntry(  0.1,   100, 400, 10 ),
                                                    new ScaleEntry(  0.25,  100, 250, 10 ),
                                                    new ScaleEntry(  0.5,   100, 200, 10 ),
                                                    new ScaleEntry(  1,     80,  200, 10 ),
                                                    new ScaleEntry(  5,     80,  400, 10 ),
                                                    new ScaleEntry( 10,     80,  160, 10 ),
                                                    new ScaleEntry( 30,     80,  240, 10 ),
                                                    new ScaleEntry( 60,     80,  160, 10 )
                                                };
        }

        #endregion

        #region Properties

        public double Unit { get { return _activeScale.Unit; } }
        public double IntervalSize {  get { return _intervalSize;  } }
        public int Division { get { return _activeScale.Division; } }

        #endregion

        #region Public functions

        public void SetUnit( double unit )
        {
            ScaleEntry entry = GetScaleForUnit( unit );
            SetActiveScale( entry );

            // set the interval size to the middle of range
            _intervalSize = entry.MinSizePx + ( entry.MaxSizePx - entry.MinSizePx ) / 2;
        }

        public void AdjustIntervalSize( double delta )
        {
            // Delta is always 120 or -120 for standard windows mouse. It can be less for more precision mouses.
            delta = delta / 120.0;
            delta *= 5;

            _intervalSize += delta;

            if ( _intervalSize < _activeScale.MinSizePx )
            {
                // change scale unit
                ScaleEntry newScaleUnit = GetNextScale();
                if ( newScaleUnit != _activeScale )
                {
                    SetActiveScale( newScaleUnit );
                    _intervalSize = _activeScale.MaxSizePx;
                }
                else
                {
                    _intervalSize = _activeScale.MinSizePx;
                }                                              
            }

            if ( _intervalSize > _activeScale.MaxSizePx )
            {
                ScaleEntry newScaleUnit = GetPreviousScale();

                if ( newScaleUnit != _activeScale )
                {
                    SetActiveScale( newScaleUnit );
                    _intervalSize = _activeScale.MinSizePx;
                }
                else
                {
                    _intervalSize = _activeScale.MaxSizePx;
                }
            }
        }

        #endregion

        #region Private functions

        private ScaleEntry GetScaleForUnit( double unit )
        {
            foreach ( ScaleEntry entry in _scalingTable )
            {
                if ( unit <= entry.Unit )
                {
                    return entry;
                }
            }

            return ( _scalingTable.Count != 0 ) ? _scalingTable[_scalingTable.Count - 1] : null;
        }

        private void SetActiveScale( ScaleEntry scale )
        {
            // check if current scale is the the last
            if ( scale != null )
            {
                _activeScale = scale;               
            }
        }
    
        private ScaleEntry GetNextScale()
        {
            int index = _scalingTable.IndexOf( _activeScale ) + 1;
            if ( index == _scalingTable.Count )
            {
                return _activeScale;
            }

            return _scalingTable[index];
        }

        private ScaleEntry GetPreviousScale()
        {
            int index = _scalingTable.IndexOf( _activeScale ) - 1;
            if ( index < 0 )
            {
                return _activeScale;
            }

            return _scalingTable[index];
        }

        private bool IsFirstScale( ScaleEntry scale )
        {
            if ( _scalingTable.Count == 0 )
            {
                return false;
            }

            return _scalingTable.IndexOf( scale ) == 0;
        }

        private bool IsLastScale( ScaleEntry scale )
        {
            if ( _scalingTable.Count == 0 )
            {
                return false;
            }

            return _scalingTable.IndexOf( scale ) == _scalingTable.Count - 1;            
        }

        #endregion
    }
}
